package dpss.node.server;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;

import dpss.commons.CustomLogger;
import dpss.commons.Player;

/**
 * The db structure to hold all player accounts indexed by their first letter of
 * their username
 * 
 * @author musab.mirza
 * 
 */
public class AccountDatabase implements Serializable{
	
	private static final long serialVersionUID = 1L;


	private static CustomLogger logger = CustomLogger.getInstance();
	
	
	// The datbaase storing all players list indexed by the first letter of
	// their username
	private HashMap<Character, ArrayList<Player>> data;

	/**
	 * Initializes the data by adding indexes (a to z)
	 */
	public AccountDatabase() {
		
		data = new HashMap<Character, ArrayList<Player>>();
		
		for (Character ch = 'a'; ch <= 'z'; ++ch) {
			ArrayList<Player> emptyList = new ArrayList<Player>();
			data.put(ch, emptyList);
		}
		
		// Add the admin account for this server
		try {
			addAccount(new Player("admin", "nimda", 20, "000.000.000.000",
					"Admin", "Dude", false));
		} catch (Exception e) {
			logger.log("ERROR - Could not add the admin account");
		}
	}

	/**
	 * Checks if an account already exists
	 * 
	 * @param username
	 * @return
	 */
	public synchronized boolean accountExists(String username) {
		Character index = username.charAt(0);
		Iterator<Player> iter = data.get(index).iterator();
		while (iter.hasNext()) {
			Player p = iter.next();
			if (p.getUsername().equalsIgnoreCase(username)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Adds a new player account to the hashmap
	 * 
	 * @param player
	 * @throws Exception
	 */
	public synchronized void addAccount(Player player) throws Exception {
		Character index = player.getUsername().charAt(0);
		if (accountExists(player.getUsername()))
			throw new Exception("User account already exists");
		data.get(index).add(player);
	}

	/**
	 * Checks if an account exists and returns the Player
	 * 
	 * @param username
	 * @return
	 * @throws Exception
	 */
	public synchronized Player getAccount(String username) throws Exception {

		if (username.isEmpty() || Character.isDigit(username.charAt(0))) {
			throw new Exception("Invalid username");
		}

		if (!accountExists(username)) {
			throw new Exception("User account does not exist");
		}

		Character index = username.charAt(0);

		Iterator<Player> iter = data.get(index).iterator();
		while (iter.hasNext()) {
			Player p = iter.next();
			if (p.getUsername().equalsIgnoreCase(username)) {
				return p;
			}
		}
		return null;
	}

	/**
	 * Tells the status of all players in this server as a string
	 * 
	 * @return The players status in this server
	 */
	public synchronized String getPlayersInfo() {
		int online = 0;
		int offline = 0;
		for (ArrayList<Player> playerList : data.values()){
			Iterator<Player> iter = playerList.iterator();
			while (iter.hasNext()) {
				Player p = iter.next();
				if (!(p.getUsername().equalsIgnoreCase("admin"))){
					if (p.isOnline()) online++;
					else offline++;
				}
			}
		}
		return  online + " online, " + offline + " offline";
	}

	public void loadTestPlayers(HashSet<Player> playersList) {
		for(Player newPlayer : playersList){
			try {
				addAccount(newPlayer);
			} catch (Exception e) {
				logger.log("ERROR - Could not add the admin account");
			}
		}
		
	}

	/**
	 * Removes an account
	 * @param usernameToSuspend
	 * @throws Exception
	 */
	public synchronized void removeAccount(String username) throws Exception {
		if (!accountExists(username)){
			throw new Exception("User account '"+username+"' does not exist or has been removed");
		}
		Character index = username.toLowerCase().charAt(0);
		Iterator<Player> iter = data.get(index).iterator();
		while (iter.hasNext()) {
			Player p = iter.next();
			if (p.getUsername().equalsIgnoreCase(username)) {
				iter.remove();
			}
		}
	}

	
	/**
	 * Checks if the account database is empty (ignoring the admin account)
	 * @return
	 */
	public synchronized boolean isEmpty() {
		
		for (ArrayList<Player> playerList : data.values()){
			Iterator<Player> iter = playerList.iterator();
			while (iter.hasNext()) {
				Player p = iter.next();
				if (!p.getUsername().equalsIgnoreCase("admin"))
					return false;
			}
		}
		return true;
	}

}
